package com.example.flex.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.example.flex.common.exception.BusinessException;
import com.example.flex.common.struct.RoleStructMapper;
import com.example.flex.common.struct.UserStructMapper;
import com.example.flex.common.types.BusinessEnum;
import com.example.flex.common.types.DelFlagEnum;
import com.example.flex.domain.*;
import com.example.flex.domain.vo.LoginUserVO;
import com.example.flex.domain.vo.RoleVO;
import com.example.flex.domain.vo.UserVO;
import com.example.flex.mapper.RoleMapper;
import com.example.flex.mapper.RoleMenuMapper;
import com.example.flex.mapper.UserMapper;
import com.example.flex.mapper.UserRoleMapper;
import com.example.flex.service.UserService;
import com.example.flex.utils.jwt.JwtUtil;
import com.example.flex.utils.redis.RedisCache;
import com.example.flex.utils.response.BaseResponse;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户表业务逻辑具体实现
 *
 * @Author: 不是菜鸡爱编程
 * @Date:
 * @Description：
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService, UserDetailsService {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;
    @Autowired
    private UserStructMapper userStructMapper;
    @Autowired
    private RoleStructMapper roleStructMapper;
    @Autowired
    private RoleMenuMapper roleMenuMapper;

    /**
     * 登录
     *
     * @param user 用户
     * @return {@link BaseResponse}<{@link ?}>
     */
    @Override
    public BaseResponse<?> login(UserEntity user) {
        // 用户名或密码为空
        if (StrUtil.isBlank(user.getUsername()) || StrUtil.isBlank(user.getPassword())) {
            throw new RuntimeException(BusinessEnum.USERNAME_OR_PASSWORD_IS_BLANK.getMsg());
        }
        // 认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
        System.out.println("authenticationToken:" + authenticationToken);
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (Objects.isNull(authenticate)) {
            throw new RuntimeException(BusinessEnum.USERNAME_OR_PASSWORD_IS_WRONG.getMsg());
        }
        //使用userid生成token
        LoginUserVO loginUser = (LoginUserVO) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String jwt = JwtUtil.createJWT(userId);
        //authenticate存入redis
        redisCache.setCacheObject("login:" + userId, loginUser);
        //把token响应给前端
        HashMap<String, Object> map = new HashMap<>();
        UserVO userVO = userStructMapper.userToUserVO(loginUser.getUser());
        List<RoleEntity> roleList = loginUser.getUser().getRoleEntityList();
        List<MenuEntity> menuEntities = new ArrayList<>();
        for (RoleEntity roleEntity : roleList) {
            menuEntities.addAll(roleEntity.getMenuEntityList());
        }
        List<RoleVO> roleVOList = roleStructMapper.roleListToRoleVOList(roleList);
        map.put("token", jwt);
        map.put("user", userVO);
        map.put("role", roleVOList);
        map.put("menu", menuEntities);
        return BaseResponse.success().setData(map);
    }

    /**
     * 按用户名加载用户
     * authenticationManager.authenticate(authenticationToken) 认证时，调用该方法
     *
     * @param username 用户名
     * @return {@link UserDetails}
     * @throws UsernameNotFoundException 用户名未发现异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<String> permissions = null;
        // 找到数据库中对应的登录用户
        UserEntity userEntity = mapper.selectOneByQuery(QueryWrapper.create().eq(UserEntity::getUsername, username));
        // 判断用户是否存在
        if (ObjUtil.isNull(userEntity)) {
            throw new BusinessException(BusinessEnum.USER_NOT_EXISTED.getMsg());
        }
        // 判断用户状态是否异常
        if (userEntity.getStatus() != 0) {
            throw new BusinessException(BusinessEnum.ABNORMAL_USER_STATUS.getMsg());
        }
        // 判断用户是否已注销
        if (!DelFlagEnum.NORMAL.getCode().equals(userEntity.getDelFlag())) {
            throw new BusinessException(BusinessEnum.USER_LOGGED_OUT.getMsg());
        }
        // 获取到用户对应的角色列表
        List<RoleEntity> roleEntityList = getRoleEntityList(userEntity);
        // 用户角色不为空
        if (CollUtil.isNotEmpty(roleEntityList)) {
            // 获取角色对应的菜单列表
            Set<MenuEntity> menuEntityList = getMenuEntityList(roleEntityList);
            // 判断菜单是否为空
            if (CollUtil.isNotEmpty(menuEntityList)) {
                permissions = menuEntityList.stream().map(MenuEntity::getPerms).collect(Collectors.toList());
            }
            userEntity.setRoleEntityList(roleEntityList);
        }
        return new LoginUserVO(userEntity, permissions);
    }

    /**
     * 获取角色实体列表
     *
     * @param userEntity 用户实体
     * @return {@link List}<{@link RoleEntity}>
     */
    private List<RoleEntity> getRoleEntityList(UserEntity userEntity) {
        List<RoleEntity> roleEntityList = new ArrayList<>();
        List<UserRoleEntity> userRoleEntities = userRoleMapper.selectListWithRelationsByQuery(QueryWrapper.create().eq(UserRoleEntity::getUserId, userEntity.getId()));
        for (UserRoleEntity userRoleEntity : userRoleEntities) {
            roleEntityList.add(userRoleEntity.getRoleEntity());
        }
        return roleEntityList;
    }

    /**
     * 获取菜单实体列表
     *
     * @param roleEntityList 角色id列表
     * @return {@link List}<{@link MenuEntity}>
     */
    private Set<MenuEntity> getMenuEntityList(List<RoleEntity> roleEntityList) {
        Set<MenuEntity> menuEntityList = new HashSet<>();
        for (RoleEntity roleEntity : roleEntityList) {
            menuEntityList.addAll(roleEntity.getMenuEntityList());
        }
        return menuEntityList;
    }
}